﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sinacor.Infra.Core.ProcessManager.Server;
using Sinacor.Infra.Service.Validation;
using Sinacor.Infra.Common.Exceptions;
using Sinacor.Infra.Service.ExceptionHandling;
using PSE.Framework.Communication.Client.File.FlatFile;
using PSE.Framework.Communication.Adapters.File;
using System.IO;
using PSE.Framework.Communication.Configuration.File.FlatFile;
using PSE.Framework.Communication.Configuration;
using PSE.Framework.Communication.Client.File.XmlFile;
using PSE.Framework.Communication.Configuration.File.XmlFile;

namespace Sinacor.Process.Bovespa.Negocio.Server.ProcessLogic
{
    public class Exportacao : BaseProcessLogic
    {
        #region Atributos

        private int _erroDataPregaoInvalida = 109762;
        private int _erroBrokerNaoCadastrado = 109831;
        private int _erroBrokerInativo = 109832;
        private int _erroBrokerBloqueado = 109833;
        private int _erroClienteNaoCadastrado = 109834;
        private int _erroClienteInativo = 109835;
        private int _erroClienteBloqueado = 109836;
        private int _erroClienteNaoFilhoteBroker = 109837;
        private int _erroExportacaoSemRegistros = 109840;

        private decimal porcentagemPorRegistro = 0;
        private int registroAtual = 0;

        #endregion

        #region Propriedades

        public int ErroDataPregaoInvalida
        {
            get { return _erroDataPregaoInvalida; }
            set { _erroDataPregaoInvalida = value; }
        }

        public int ErroBrokerNaoCadastrado
        {
            get { return _erroBrokerNaoCadastrado; }
            set { _erroBrokerNaoCadastrado = value; }
        }

        public int ErroBrokerInativo
        {
            get { return _erroBrokerInativo; }
            set { _erroBrokerInativo = value; }
        }

        public int ErroBrokerBloqueado
        {
            get { return _erroBrokerBloqueado; }
            set { _erroBrokerBloqueado = value; }
        }

        public int ErroClienteNaoCadastrado
        {
            get { return _erroClienteNaoCadastrado; }
            set { _erroClienteNaoCadastrado = value; }
        }

        public int ErroClienteInativo
        {
            get { return _erroClienteInativo; }
            set { _erroClienteInativo = value; }
        }

        public int ErroClienteBloqueado
        {
            get { return _erroClienteBloqueado; }
            set { _erroClienteBloqueado = value; }
        }

        public int ErroClienteNaoFilhoteBroker
        {
            get { return _erroClienteNaoFilhoteBroker; }
            set { _erroClienteNaoFilhoteBroker = value; }
        }

        public int ErroExportacaoSemRegistros
        {
            get { return _erroExportacaoSemRegistros; }
            set { _erroExportacaoSemRegistros = value; }
        }

        #endregion
        
        #region Construtor

        /// <summary>
        /// Construtor padrão
        /// </summary>
        public Exportacao()
        {
        }

        #endregion

        #region Eventos
        public event EventHandler ExportacaoConcluida;
        #endregion

        #region Métodos Públicos

        /// <summary>
        /// Execute
        /// </summary>
        public void ExportarNegocios(ProcessEntity.Parameter.FiltroExportacaoNegocio entity)
        {
            Communication.CalendarioBolsa communicationCalendarioBolsa;
            Communication.CalendarioOrdens communicationCalendarioOrdens;
            Communication.Cliente communicationCliente;
            ResourceAccess.Exportacao raExportacao;
            List<ProcessEntity.Calendario> calendario = null;
            List<ProcessEntity.NegocioSDetail> retorno = null;
            ResourceAccess.ImportacaoExportacao raImportacaoExportacao = new ResourceAccess.ImportacaoExportacao();
            Int32 codigoEmpresaBolsa = 0;
            ProcessEntity.Cliente broker = null;
            ProcessEntity.Cliente cliente = null;

            communicationCliente = new Communication.Cliente();
            communicationCalendarioBolsa = new Communication.CalendarioBolsa();
            communicationCalendarioOrdens = new Communication.CalendarioOrdens();
            raExportacao = new ResourceAccess.Exportacao();


            //Valida RuleSet
            SinacorValidation validation = new SinacorValidation();
            validation.Validate<ProcessEntity.Parameter.FiltroExportacaoNegocio>(entity);

            ValidationException validationException = new ValidationException();

            //Validar bloker
            broker = communicationCliente.ObterCliente(new ProcessEntity.Parameter.FiltroCliente { CodigoCliente = entity.CodigoBroker });

            if (broker == null)
                validationException.AddError(this.ErroBrokerNaoCadastrado);

            //Validar cliente
            if (entity.CodigoBrokerCliente > 0)
            {
                cliente = communicationCliente.ObterCliente(new ProcessEntity.Parameter.FiltroCliente { CodigoCliente = entity.CodigoBrokerCliente });

                if (cliente == null)
                    validationException.AddError(this.ErroClienteNaoCadastrado);
                else
                {
                    if(cliente.Broker != entity.CodigoBroker)
                        validationException.AddError(this.ErroClienteNaoFilhoteBroker);
                }
            }

            if (validationException.ValidationErrors.Count > 0)
                throw validationException;

            //Verifica se tipo de sistema igual a "Bolsa"
            if (entity.TipoSistema == ProcessEntity.Enum.TipoSistema.Bolsa)
            {
                //Consulta calendario Bolsa
                calendario = communicationCalendarioBolsa.ConsultarCalendarios(
                    new ProcessEntity.Parameter.FiltroCalendario
                    {
                        DataPregao = entity.DataPregao.Value
                    });
            }
            else
            {
                //Consulta calendario Ordens
                calendario = communicationCalendarioOrdens.ConsultarCalendarios(
                    new ProcessEntity.Parameter.FiltroCalendario
                    {
                        DataPregao = entity.DataPregao.Value
                    });
            }

            //Verifica se data pregao é válida
            if (calendario == null)
                throw new BusinessException(this.ErroDataPregaoInvalida);

            //Consulta Negocios para exportar
            retorno = raExportacao.ConsultarNegociosExportar(entity);

            if (retorno.Count == 0)
                throw new BusinessException(this.ErroExportacaoSemRegistros);

            if (retorno.Count != 0)
                porcentagemPorRegistro = 100M / (decimal)retorno.Count;

            //Recupera o código empresa bolsa
            codigoEmpresaBolsa = raImportacaoExportacao.ObterCodigoEmpresaBolsa();

            //Verifica o tipo de arquivo que será exportado
            if (entity.TipoFormatoArquivo == ProcessEntity.Enum.TipoFormatoArquivo.XML)
                this.ExportarXML(entity, retorno, codigoEmpresaBolsa);
            else
                this.ExportarTxt(entity, retorno, codigoEmpresaBolsa);

            //Conclui exportação
            if (ExportacaoConcluida != null)
                ExportacaoConcluida(this, new EventArgs());
        }

        #endregion

        #region Métodos Privados

        private void AtualizaStatus(int progress, string resourceKey)
        {
            this.Status.Progress = progress;
            this.Status.Step = resourceKey;
            this.SendStatus();
        }

        private void ExportarXML(ProcessEntity.Parameter.FiltroExportacaoNegocio entity, List<ProcessEntity.NegocioSDetail> listaExportacao, int codigoEmpresaBolsa)
        {
            Layout.NegocioXMLEvento negocioXMLEvento = null;
            Layout.NegocioXMLIdentificador negocioXMLIdentificador = null;
            List<Layout.NegocioXMLNegocio> listaNegocioXML = null;
            Translator.ProcessEntityToLayout.Negocio translatorToLayout = new Translator.ProcessEntityToLayout.Negocio();
            
            using (XmlFileOutputClient clientXML = new XmlFileOutputClient())
            {
                try
                {
                    //Abri arquivo
                    clientXML.Open("ExportacaoNegSXML");

                    XmlFileOutputConfiguration _config = CommunicationConfigurationManager.GetInstance().GetCommunicationClientConfiguration("ExportacaoNegSXML") as XmlFileOutputConfiguration;

                    FileInfo fi = new FileInfo(entity.NomeArquivo);
                    clientXML.OpenFile(new FileOpenParameter(_config == null ? null : _config.OutputPath,
                                                          fi.Name));


                    listaNegocioXML = new List<Layout.NegocioXMLNegocio>();

                    foreach (ProcessEntity.NegocioSDetail item in listaExportacao)
                    {
                        if (IsAborted)
                            break;

                        if (item.BolsaMovimento == ProcessEntity.Enum.TipoBolsaMovimento.Bovespa)
                            item.BolsaNegocio = 1;
                        else if (item.BolsaMovimento == ProcessEntity.Enum.TipoBolsaMovimento.Soma)
                            item.BolsaNegocio = 5;

                        if (item.TipoMercado != ProcessEntity.Enum.TipoMercado.ExercicioOpcaoCompra &&
                            item.TipoMercado != ProcessEntity.Enum.TipoMercado.ExercicioOpcaoVenda)
                        {
                            item.CodigoCliente = 0;
                            item.DigitoCliente = 0;
                        }

                        //Traduz negocio para layout e adiciona na lista
                        listaNegocioXML.Add(translatorToLayout.ToNegocioXMLNegocio(item));

                        registroAtual++;

                        //Atualiza status
                        SendStatus(Convert.ToInt32(Math.Floor(porcentagemPorRegistro * registroAtual)), "Bovespa.ImportacaoExportacao.Message.ExportandoArquivo");
                    }

                    //Monta evento
                    negocioXMLEvento = new Sinacor.Process.Bovespa.Negocio.Server.Layout.NegocioXMLEvento
                    {
                        DataPregao = entity.DataPregao.Value.ToShortDateString(),
                        Negocio = listaNegocioXML
                    };

                    //Monta identificador
                    negocioXMLIdentificador = new Sinacor.Process.Bovespa.Negocio.Server.Layout.NegocioXMLIdentificador
                    {
                        CodigoArquivo = "NEGS",
                        CodigoUsuario = codigoEmpresaBolsa,
                        DataGeracao = DateTime.Now.ToString(),
                        Evento = negocioXMLEvento
                    };

                    //Escreve tudo
                    clientXML.WriteRecord(negocioXMLIdentificador, "NegocioXMLIdentificador");

                    //Fecha Arquivo
                    clientXML.CloseFile();
                }
                catch
                {
                    if (clientXML.Status == PSE.Framework.Communication.CommunicationStatus.Opened)
                        clientXML.CancelFile();

                    throw;
                }
            }
        }

        private void ExportarTxt(ProcessEntity.Parameter.FiltroExportacaoNegocio entity, List<ProcessEntity.NegocioSDetail> listaExportacao, int codigoEmpresaBolsa)
        {
            Translator.ProcessEntityToLayout.Negocio translatorToLayout = new Translator.ProcessEntityToLayout.Negocio();
            Layout.NegocioSDetail layoutNegocioSDetail = null;
            Layout.NegocioHeader headerNeg = null;
            Layout.NegocioTrailer trailerNeg = null;
            
            using (FlatFileOutputClient clientTxt = new FlatFileOutputClient())
            {
                try
                {
                    //Abri arquivo
                    clientTxt.Open("ExportacaoNegS");


                    FlatFileOutputConfiguration _config = CommunicationConfigurationManager.GetInstance().GetCommunicationClientConfiguration("ExportacaoNegS") as FlatFileOutputConfiguration;

                    FileInfo fi = new FileInfo(entity.NomeArquivo);
                    clientTxt.OpenFile(new FileOpenParameter(_config == null ? null : _config.OutputPath,
                                                          fi.Name));


                    //Monta header
                    headerNeg = new Sinacor.Process.Bovespa.Negocio.Server.Layout.NegocioHeader
                    {
                        CodigoArquivo = "NEGS",
                        CodigoDestino = codigoEmpresaBolsa,
                        CodigoOrigem = "SINACOR",
                        CodigoUsuario = codigoEmpresaBolsa,
                        DataGeracao = DateTime.Now,
                        DataPregao = entity.DataPregao.Value,
                        Reserva = string.Empty,
                        TipoRegistro = "00"
                    };

                    //Monta trailer
                    trailerNeg = new Sinacor.Process.Bovespa.Negocio.Server.Layout.NegocioTrailer
                    {
                        CodigoArquivo = "NEGS",
                        CodigoDestino = codigoEmpresaBolsa.ToString(),
                        CodigoOrigem = "SINACOR",
                        CodigoUsuario = codigoEmpresaBolsa,
                        DataGeracao = DateTime.Now,
                        Reserva = string.Empty,
                        TipoRegistro = "99",
                        TotalRegistrosGerados = listaExportacao.Count + 2
                    };

                    //Escreve header no arquivo
                    clientTxt.WriteRecord(headerNeg, "NegocioHeader");

                    foreach (ProcessEntity.NegocioSDetail item in listaExportacao)
                    {
                        if (IsAborted)
                            break;

                        if (item.BolsaMovimento == ProcessEntity.Enum.TipoBolsaMovimento.Bovespa)
                            item.BolsaNegocio = 1;
                        else if (item.BolsaMovimento == ProcessEntity.Enum.TipoBolsaMovimento.Soma)
                            item.BolsaNegocio = 5;

                        if (item.TipoMercado != ProcessEntity.Enum.TipoMercado.ExercicioOpcaoCompra &&
                            item.TipoMercado != ProcessEntity.Enum.TipoMercado.ExercicioOpcaoVenda)
                        {
                            item.CodigoCliente = 0;
                            item.DigitoCliente = 0;
                        }

                        //Traduz negocio para layout
                        layoutNegocioSDetail = translatorToLayout.ToNegocioSDetail(item);

                        //Escreve registro
                        if (layoutNegocioSDetail != null)
                            clientTxt.WriteRecord(layoutNegocioSDetail, "NegocioSDetail");

                        registroAtual++;

                        //Atualiza status
                        SendStatus(Convert.ToInt32(Math.Floor(porcentagemPorRegistro * registroAtual)), "Bovespa.ImportacaoExportacao.Message.ExportandoArquivo");
                    }

                    //Escreve trailer no arquivo
                    clientTxt.WriteRecord(trailerNeg, "NegocioTrailer");

                    //Fecha Arquivo
                    clientTxt.CloseFile();
                }
                catch
                {
                    if (clientTxt.Status == PSE.Framework.Communication.CommunicationStatus.Opened)
                        clientTxt.CancelFile();

                    throw;
                }
            }
        }

        #endregion
    }
}